package models

import (
	"fmt"
	"time"

	_ "github.com/jinzhu/gorm/dialects/mysql"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
	"gorm.io/plugin/soft_delete"

	"wowCoupon.com/setting"
)

var db *gorm.DB
var err error

// Model will embedded by other common models
type Model struct {
	ID        int64                 `json:"id" gorm:"primaryKey"`
	CreatedAt int64                 `json:"created_at"`
	UpdatedAt int64                 `gorm:"column:updated_at;default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP"`
	DeletedAt soft_delete.DeletedAt `json:"deleted_at"`
}

// Setup initializes the database instance
func Setup() {
	dsn := fmt.Sprintf(
		"%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True",
		setting.DatabaseSetting.User,
		setting.DatabaseSetting.Password,
		setting.DatabaseSetting.Host,
		setting.DatabaseSetting.Name,
	)

	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			TablePrefix: setting.DatabaseSetting.TablePrefix, // table name prefix, table for `User` would be `t_users`
		},
	})

	if err != nil {
		panic(fmt.Sprintf("models.Setup err: %v", err))
	}

	// Auto migrate tables
	autoMigrateTables()

	//Register and replace global callbacks
	/*
		db.Callback().Create().Replace("gorm:update_time_stamp", updateTimeStampForCreateCallback)*/
	//db.Callback().Update().Replace("gorm:update_time_stamp", updateTimeStampForUpdateCallback)
	/*db.Callback().Delete().Replace("gorm:delete", deleteCallback)
	 */

	sqlDB, err := db.DB()
	if err != nil {
		panic(fmt.Sprintf("models.Setup err: %v", err))
	}

	//Set max number of connections
	sqlDB.SetMaxIdleConns(10)
	sqlDB.SetMaxOpenConns(100)
	sqlDB.SetConnMaxLifetime(30 * time.Minute)
}

// Auto migrate table if table does not exist
func autoMigrateTables() {
	tables := []interface{}{
		CouponMeta{},
		CouponRecord{},
		User{},
	}

	for _, t := range tables {
		if ok := db.Migrator().HasTable(&t); !ok {
			db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&t)
		}
	}
}

//CloseDB closes database connection (unnecessary)
/*
func CloseDB() {
	defer db.Close()
}
*/

/*
// updateTimeStampForCreateCallback will set `CreatedOn`, `ModifiedOn` when creating
func updateTimeStampForCreateCallback(db *gorm.DB) {
	if db.Error != nil {
		nowTime := time.Now().Unix()
		if createdTimeField := db.Statement.Schema.LookUpField("CreatedOn") {
			createdTimeField.ReflectValueOf
			if createTimeField.IsBlank {
				createTimeField.Set(nowTime)
			}
		}

		if modifyTimeField, ok := db.FieldByName("ModifiedOn"); ok {
			if modifyTimeField.IsBlank {
				modifyTimeField.Set(nowTime)
			}
		}
	}
}

// updateTimeStampForUpdateCallback will set `ModifiedOn` when updating
func updateTimeStampForUpdateCallback(db *gorm.DB) {
	if _, ok := db.Get("gorm:update_column"); !ok {
		db.Update("UpdatedAt", time.Now().Unix())
	}
}


// deleteCallback will set `DeletedOn` where deleting
func deleteCallback(scope *gorm.Scope) {
	if !scope.HasError() {
		var extraOption string
		if str, ok := scope.Get("gorm:delete_option"); ok {
			extraOption = fmt.Sprint(str)
		}

		deletedOnField, hasDeletedOnField := scope.FieldByName("DeletedOn")

		if !scope.Search.Unscoped && hasDeletedOnField {
			scope.Raw(fmt.Sprintf(
				"UPDATE %v SET %v=%v%v%v",
				scope.QuotedTableName(),
				scope.Quote(deletedOnField.DBName),
				scope.AddToVars(time.Now().Unix()),
				addExtraSpaceIfExist(scope.CombinedConditionSql()),
				addExtraSpaceIfExist(extraOption),
			)).Exec()
		} else {
			scope.Raw(fmt.Sprintf(
				"DELETE FROM %v%v%v",
				scope.QuotedTableName(),
				addExtraSpaceIfExist(scope.CombinedConditionSql()),
				addExtraSpaceIfExist(extraOption),
			)).Exec()
		}
	}
}

// addExtraSpaceIfExist adds a separator
func addExtraSpaceIfExist(str string) string {
	if str != "" {
		return " " + str
	}
	return ""
}
*/
